/*
 * main.cpp
 *
 *  Created on: Dec 30, 2011
 *      Author: kkb110
 */

#include "common_includes.h"
#include "normal_distribution.h"
#include "model.h"
#include "trainer.h"
#include "trainer_sequence_wise.h"
#include "maximum_likelihood_trainer.h"

class Data{
public:
	vector<vector<Shooter>> shooters;
	vector<vector<Model<double, 2, 2>::Shot>> shots;
	void append(Data to_append){
		for (int i = 0; i < to_append.shooters.size(); ++i){
			shooters.push_back(to_append.shooters[i]);
			shots.push_back(to_append.shots[i]);
		}
	}
};

Model<double, 2, 2> get_answer_model(){
	Model<double, 2, 2> answer;
	answer.shooting_distributions[0].mean << 0, 0;
	answer.shooting_distributions[0].covariance << 1, 0, 0, 1;

//	answer.transition_probabilities << 0.3, 0.7, 0.5, 0.5;
//	answer.shooting_distributions[1].mean << -1, 0;
//	answer.shooting_distributions[1].covariance << 2, 1, 1, 1;

	answer.transition_probabilities << 0.4, 0.6, 0.5, 0.5;
	answer.shooting_distributions[1].mean << -1, 1;
	answer.shooting_distributions[1].covariance << 2, 1.2, 1.2, 1;

//	answer.transition_probabilities << 0.3, 0.7, 0.5, 0.5;
//	answer.shooting_distributions[1].mean << -1, 0;
//	answer.shooting_distributions[1].covariance << 1, 0, 0, 1;

	return answer;
}

void draw_answer(){
	Model<double, 2, 2> answer = get_answer_model();
	auto tmp = answer.simulate(1000);
	auto shooters1 = tmp.first;
	auto shots1 = tmp.second;
	auto tmp2 = answer.simulate(1000);
	auto shooters2 = tmp2.first;
	auto shots2 = tmp2.second;

	decltype(shots1) shots_type1;
	decltype(shots1) shots_type2;
	for (size_t i = 0; i < shots1.size(); ++i){
		switch (shooters1[i]) {
		case 0:
			shots_type1.push_back(shots1[i]);
			break;
		case 1:
			shots_type2.push_back(shots1[i]);
			break;
		}
	}
	answer.save("./plot/test");
	save_eigens(shots_type1, "./plot/test_shots1");
	save_eigens(shots_type2, "./plot/test_shots2");

}

void unit_test3(){
	TrainerSequenceWise trainer;
	Model<double, 2, 2> answer = get_answer_model();
	for (int i = 0; i < 100; ++i){
		auto tmp = answer.simulate(5);
		trainer.shooters.push_back(tmp.first);
		trainer.shots.push_back(tmp.second);
	}
	Model<double, 2, 2> model = trainer.run();

	auto shooters_to_evaluate = trainer.shooters;
	auto shots_to_evaluate = trainer.shots;

	int total_score = 0;
	for (int i = 0; i < shooters_to_evaluate.size(); ++i){
		auto test = model.best_path(shots_to_evaluate[i]);

		total_score += 1;
		for (int t = 0; t < shooters_to_evaluate[i].size(); ++t){
			if (test[t] != shooters_to_evaluate[i][t]){
				total_score -= 1;
				break;
			}
		}
//		total_score += model.path_similarity(
//				model.best_path(shots_to_evaluate[i]), shooters_to_evaluate[i]);
//		cout << total_score << endl;
	}
	cout << "trained score is " << total_score << endl;

	total_score = 0;
	for (int i = 0; i < shooters_to_evaluate.size(); ++i){
		auto test = answer.best_path(shots_to_evaluate[i]);

		total_score += 1;
		for (int t = 0; t < shooters_to_evaluate[i].size(); ++t){
			if (test[t] != shooters_to_evaluate[i][t]){
				total_score -= 1;
				break;
			}
		}
	}
	cout << "answer score is " << total_score << endl;
}

void unit_test2(){
	Trainer trainer;
	MaximumLikelihoodTrainer trainer_MLE;
	Model<double, 2, 2> answer = get_answer_model();

	for (int i = 0; i < 1; ++i){
		auto tmp = answer.simulate(1000);
		trainer.shooters.push_back(tmp.first);
		trainer.shots.push_back(tmp.second);
	}
	Model<double, 2, 2> model = trainer.run();
	Model<double, 2, 2> model_MLE = trainer_MLE.run();

	auto shots_to_test = trainer.shots[0];
	auto shooters_to_test = trainer.shooters[0];

	auto forward_backward = model.forward_backward(shots_to_test, 1);
	cout
			<< "framewise : "
			<< model.path_similarity(model.best_shooters(forward_backward),
					shooters_to_test) << endl;

	forward_backward = answer.forward_backward(shots_to_test, 1);
	cout
			<< "answer : "
			<< answer.path_similarity(answer.best_shooters(forward_backward),
					shooters_to_test) << endl;

	forward_backward = model_MLE.forward_backward(shots_to_test, 1);
	model_MLE.save("./plot/MLE");
	cout
			<< "MLE : "
			<< answer.path_similarity(model_MLE.best_shooters(forward_backward),
					shooters_to_test) << endl;

}

void unit_test1(){
	Model<double, 2, 2> answer = get_answer_model();
	MaximumLikelihoodTrainer trainer;
	for (int i = 0; i < 1; ++i){
		auto tmp = answer.simulate(1000);
		trainer.shooters.push_back(tmp.first);
		trainer.shots.push_back(tmp.second);
	}
	trainer.run();
}

void unit_test_normaldistribution(){
	NormalDistribution<double, 1> normal;
	normal.covariance << 2;
	normal.mean << 0;

	//section test
	double interval = 0.1;
	double accum = 0;
	double start = 0;
	double end = 1;
	for (double x = start; x < end; x += interval){
		Eigen::Array<double, 1, 1> xx;
		xx << x;
		auto pdf_current = normal.probability_density(xx);
		auto pdf_next = normal.probability_density(xx + interval);
		auto pdf_size = abs((pdf_next + pdf_current) / 2 * interval);
		cout << accum << "\t";
		accum += pdf_size;
	}
	cout << endl; //<< accum;
	random_device seed;
	mt19937 random_number_engine(seed());
	auto points = normal.generate_points(100000, random_number_engine);
	accum = 0;
	for (double x = start; x < end; x += interval){
		double count = 0;
		for (int i = 0; i < points.size(); ++i){
			if (x < points[i](0) && points[i](0) < x + interval){
				++count;
			}
		}
		cout << accum << "\t";
		accum += count / points.size();
	}
}
void to_publish_framewise(){
	//generate k fold data set
	int k = 4;
	int sequence_length = 100;
	vector<Data> k_fold_data(4);

	Model<double, 2, 2> answer = get_answer_model();
	for (int i = 0; i < k; ++i){
		auto tmp = answer.simulate(sequence_length);
		k_fold_data[i].shooters.push_back(tmp.first);
		k_fold_data[i].shots.push_back(tmp.second);
	}

	for (int i = 0; i < k; ++i){
		//prepare correct data
		Data data_training;
		Data data_test;
		for (int j = 0; j < k; ++j){
			if (j == i){
				data_test.append(k_fold_data[j]);
			}else{
				data_training.append(k_fold_data[j]);
			}
		}

		//train models
		vector<Model<double, 2, 2>> models(5);

		models[0] = answer;

		MaximumLikelihoodTrainer trainer_MLE;
		trainer_MLE.shooters = data_training.shooters;
		trainer_MLE.shots = data_training.shots;
		models[1] = trainer_MLE.run();

		Trainer trainer_framewise;
		trainer_framewise.shooters = data_training.shooters;
		trainer_framewise.shots = data_training.shots;
		trainer_framewise.eta = 1;
		models[2] = trainer_framewise.run();
		trainer_framewise.eta = 2;
		models[3] = trainer_framewise.run();
		trainer_framewise.eta = 3;
		models[4] = trainer_framewise.run();

		for (int j = 0; j < models.size(); ++j){
			//run models on training data
			models[j];
			auto forward_backward = models[j].forward_backward(
					data_training.shots[0], 1);
			auto score_training = models[j].path_similarity(
					models[j].best_shooters(forward_backward),
					data_training.shooters[0]);
			cout << score_training << '\t';

			//run models on test data
			forward_backward = models[j].forward_backward(data_test.shots[0],
					1);
			auto score_test = models[j].path_similarity(
					models[j].best_shooters(forward_backward),
					data_test.shooters[0]);
			cout << score_test << endl;
		}
		cout << endl;
	}
}

int main(){
	draw_answer();
//	to_publish_framewise();
//	unit_test_normaldistribution();
//	unit_test1();
	unit_test2();
//	unit_test3();

}
